package com.blue.http.annotation;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;

import com.alibaba.fastjson.JSON;
import com.blue.http.message.WebsocketRequest;

/**
 * Websocket解析类
 * 
 * @author zhengj
 * @since 1.0 2016年12月15日
 */
public class WebsocketParser
{
	private static Logger logger = LoggerFactory.getLogger(WebsocketParser.class);

	private ApplicationContext applicationContext;
	private Map<WebsocketUrlConfig, Method> methodCache = new HashMap<>();
	private Map<WebsocketUrlConfig, Class<? extends WebsocketRequest>> requestCache = new HashMap<>();
	private Set<Class<?>> clazzSet = new HashSet<>();

	private static WebsocketParser instance = new WebsocketParser();

	private WebsocketParser()
	{
	}

	public static WebsocketParser getInstance()
	{
		return instance;
	}

	public void setApplicationContext(ApplicationContext applicationContext)
	{
		this.applicationContext = applicationContext;
	}

	public void parse(Class<?> clazz)
	{
		if (clazzSet.contains(clazz))
			return;

		Websocket annoWebsocket = clazz.getAnnotation(Websocket.class);
		if (annoWebsocket == null)
			throw new IllegalArgumentException(clazz.getName() + " 缺少 @Websocket 注解");

		for (Method method : clazz.getDeclaredMethods())
		{
			if (method.getModifiers() != Modifier.PUBLIC)
				continue;
			
			this.parseMethod(method, annoWebsocket);
		}

		clazzSet.add(clazz);
	}

	private void parseMethod(Method method, Websocket annoWebsocket)
	{
		Websocket websocketMethod = method.getAnnotation(Websocket.class);
		StringBuilder url = new StringBuilder(32);
		if (!annoWebsocket.url().startsWith("/"))
			url.append("/");
		url.append(annoWebsocket.url());
		Class<? extends WebsocketRequest> requestClazz = annoWebsocket.request();

		if (websocketMethod != null) // 方法配置覆盖类配置
		{
			if (!websocketMethod.url().startsWith("/"))
				url.append("/");
			url.append(websocketMethod.url());
			
			if (websocketMethod.request() != WebsocketRequest.class)
				requestClazz = websocketMethod.request();
		}

		WebsocketUrlConfig config = new WebsocketUrlConfig(url.toString());
		if (methodCache.containsKey(config))
			throw new IllegalArgumentException("已经存在：" + config);
		
		methodCache.put(config, method);
		requestCache.put(config, requestClazz);
		logger.info("找到 Websocket 服务：url={}, controller={}, method={}",
				url, method.getDeclaringClass().getSimpleName(), method.getName());
	}

	public Object invoke(String url, String request) throws Exception
	{
		WebsocketUrlConfig config = new WebsocketUrlConfig(url);
		Method method = methodCache.get(config);
		logger.debug("找到配置，config={}，method={}", config, method);
		if (method == null)
			throw new IllegalArgumentException("找不到URL：" + url);

		Class<? extends WebsocketRequest> requestClazz = requestCache.getOrDefault(config, WebsocketRequest.class);
		WebsocketRequest wsRequest = JSON.parseObject(request, requestClazz);
		Object object = applicationContext.getBean(method.getDeclaringClass());
		Object value = method.invoke(object, wsRequest);
		return value;
	}

}
